package de.riceman.simpleGo.players;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Random;

import javax.activation.UnsupportedDataTypeException;

import org.springframework.richclient.application.Application;

import de.riceman.simpleGo.events.SimpleGoLifecycleApplicationEvent;
import de.riceman.simpleGo.exceptions.IllegalMoveException;
import de.riceman.simpleGo.main.BitMatrix;
import de.riceman.simpleGo.main.Configuration;
import de.riceman.simpleGo.main.GoGame;


/**
 * An implementation of an automatic move generator implementing minimax search and
 * alpha beta pruning.
 * Future implementations:
 * 	- Zobrist hashtables to recognize redundant configurations
 *  - sorting of possible moves to improve alpha beta pruning performance
 * 
 * @author Riceman
 */
public class FirstSimpleAI extends AI {
	

	private static int analyzedPositions;
	private int iterations;
	private TemperatureMatrix temperature;
	
	/**
	 * Constructor for this class.
	 * 
	 * @param player this engine's player
	 * @param iterations this engine's number of iterations
	 */
	public FirstSimpleAI(String name, boolean player, Configuration configuration) {
		super(name, player, configuration);
		analyzedPositions = 0;
	}
	
	public void generateMove() {

		AbstractSet<BitMatrix> myGroups, opponentsGroups;
		Configuration config;
		
		//Initialize TemperatureMatrix with 0.5 for each vacant intersection.
		temperature = new TemperatureMatrix(configuration.getSize());
		
		BitMatrix possibilities = configuration.getRegularMoves(player);
		
		//Set all irregular moves to zero.
		BitMatrix m = BitMatrix.not(possibilities);
		temperature.apply(m, 0.0);
		
		Application.instance().getApplicationContext().publishEvent(
				new SimpleGoLifecycleApplicationEvent(SimpleGoLifecycleApplicationEvent.AI_THINKS_OUT_LOUD, 
						"Temperature after eliminating irregular moves: \n" + temperature.toString() + "\n"));
		
		
		//Don't close real eyes.
		if (this.player) {
			myGroups = configuration.getDarkPartitions();
		} else {
			myGroups = configuration.getLightPartitions();
		}
		
		Iterator<BitMatrix> it = myGroups.iterator();
		Iterator<BitMatrix> eyeIt;
		BitMatrix eye, group;
		
		while (it.hasNext()) {
			try {
				group = it.next();
				
				eyeIt = group.getChildren().iterator();
				
				while (eyeIt.hasNext()) {
					eye = eyeIt.next();	
					eye.intersect(possibilities);
					
					if (eye.getNumberOfSetBits() == 1) {
						temperature.apply(eye, 0.0);					
					} else {
						temperature.apply(eye, 0.25);					
					}
				}
			} catch (UnsupportedDataTypeException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
		//Don't do any move that would leave one of your groups with only one liberty.
		it = temperature.getNonZeroValues().iterator();
		
		while (it.hasNext()) {
			group = it.next();
			
			try {
				if (!group.isZeroMatrix()) {
					config = (Configuration) configuration.clone();
					
						config.doMove(player, group);
					
					
					if (config.getLibertiesOfPartition(
							configuration.getResultingPartitionForMove(player, group))
							.getNumberOfSetBits() == 1) {
						temperature.apply(group, 0.1);
					}
				}
			} catch (UnsupportedDataTypeException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalMoveException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		Application.instance().getApplicationContext().publishEvent(
				new SimpleGoLifecycleApplicationEvent(SimpleGoLifecycleApplicationEvent.AI_THINKS_OUT_LOUD, 
						"Temperature after removing moves that would leave a group with only one liberty: \n" + temperature.toString() + "\n" + temperature.getWinners().toString() + "\n"));		
		
		//Check if your opponent has a group with only one liberty.
		
		if (this.player) {
			opponentsGroups = configuration.getLightPartitions();
		} else {
			opponentsGroups = configuration.getDarkPartitions();
		}
		
		it = opponentsGroups.iterator();
		
		while (it.hasNext()) {
			try {
				group = it.next();
				BitMatrix liberties = configuration.getLibertiesOfPartition(group);
				liberties.intersect(possibilities);
				
				if (liberties.getNumberOfSetBits() == 1) {
					temperature.apply(liberties, 0.6);
				}
			} catch (UnsupportedDataTypeException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		Application.instance().getApplicationContext().publishEvent(
				new SimpleGoLifecycleApplicationEvent(SimpleGoLifecycleApplicationEvent.AI_THINKS_OUT_LOUD, 
						"Temperature after searching for easy ones: \n" + temperature.toString() + "\n" + temperature.getWinners().toString() + "\n"));
		
		//That's it. Really stupid. Just don't accept passes if there are some values > 0
		//in the temperature matrix
		if (temperature.isEmpty()) {
			possibilities = new BitMatrix(possibilities.getRows(), possibilities.getColumns());
		} else {
			temperature.setZeroMatrixTemperature(0.0);
			possibilities = temperature.getWinners().getRandomBitAsBitMatrix();
			
			while (possibilities.isZeroMatrix()) {
				possibilities = temperature.getWinners().getRandomBitAsBitMatrix();
			}
		}

		
		try {
			Thread.sleep(200);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		Application.instance().getApplicationContext().publishEvent(
				new SimpleGoLifecycleApplicationEvent(SimpleGoLifecycleApplicationEvent.AI_THINKS_OUT_LOUD, "\nCalculated the following move: \n" + possibilities.toString()));
		
		Application.instance().getApplicationContext().publishEvent(
				new SimpleGoLifecycleApplicationEvent(SimpleGoLifecycleApplicationEvent.MOVE_GENERATED, possibilities));
		
	}
	
/**
 * This method implements the minimax algorithm including alpha beta pruning.
 * 
 * @param board the chessboard
 * @param iterations remaining number of iterations
 * @param alpha alpha value
 * @param beta beta value
 * @return configuration value
 *//*
private int Minimax (GoView board, int iterations, int alpha, int beta) {

		return 0;
	}


	 (non-Javadoc)
	 * @see KI#getConfigurationValue(Board)
	 
	public int getConfigurationValue (GoView board) {

		return 0;
	}
	
	
	
	
	*//**
	 * This method calculates the material value of the board's configuration.
	 * 
	 * @param board the board
	 * @return the material value
	 *//*
	public int getMaterialValue(GoView board) {
		
		return 0;
	}*/

	@Override
	public int getConfigurationValue() {
		// TODO Auto-generated method stub
		return 0;
	}
}
